/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2013 Live Networks, Inc.  All rights reserved.
// RTP Sinks
// Implementation

#include "RTPSink.hh"
#include "GroupsockHelper.hh"

////////// RTPSink //////////

Boolean RTPSink::lookupByName(UsageEnvironment& env, char const* sinkName,
							  RTPSink*& resultSink) {
								  resultSink = NULL; // unless we succeed

								  MediaSink* sink;
								  if (!MediaSink::lookupByName(env, sinkName, sink)) return False;

								  if (!sink->isRTPSink()) {
									  env.setResultMsg(sinkName, " is not a RTP sink");
									  return False;
								  }

								  resultSink = (RTPSink*)sink;
								  return True;
}

Boolean RTPSink::isRTPSink() const {
	return True;
}

RTPSink::RTPSink(UsageEnvironment& env,
				 Groupsock* rtpGS, unsigned char rtpPayloadType,
				 unsigned rtpTimestampFrequency,
				 char const* rtpPayloadFormatName,
				 unsigned numChannels)
				 : MediaSink(env), fRTPInterface(this, rtpGS),
				 fRTPPayloadType(rtpPayloadType),
				 fPacketCount(0), fOctetCount(0), fTotalOctetCount(0),
				 fTimestampFrequency(rtpTimestampFrequency), fNextTimestampHasBeenPreset(False), fEnableRTCPReports(True),
				 fNumChannels(numChannels) {
					 fRTPPayloadFormatName
						 = strDup(rtpPayloadFormatName == NULL ? "???" : rtpPayloadFormatName);
					 gettimeofday(&fCreationTime, NULL);
					 fTotalOctetCountStartTime = fCreationTime;
					 resetPresentationTimes();

					 fSeqNo = (u_int16_t)our_random();
					 fSSRC = our_random32();
					 fTimestampBase = our_random32();

					 fTransmissionStatsDB = new RTPTransmissionStatsDB(*this);
}

RTPSink::~RTPSink() {
	delete fTransmissionStatsDB;
	delete[] (char*)fRTPPayloadFormatName;
}

u_int32_t RTPSink::convertToRTPTimestamp(struct timeval tv) {
	// Begin by converting from "struct timeval" units to RTP timestamp units:
	u_int32_t timestampIncrement = (fTimestampFrequency*tv.tv_sec);
	timestampIncrement += (u_int32_t)(fTimestampFrequency*(tv.tv_usec/1000000.0) + 0.5); // note: rounding

	// Then add this to our 'timestamp base':
	if (fNextTimestampHasBeenPreset) {
		// Make the returned timestamp the same as the current "fTimestampBase",
		// so that timestamps begin with the value that was previously preset:
		fTimestampBase -= timestampIncrement;
		fNextTimestampHasBeenPreset = False;
	}

	u_int32_t const rtpTimestamp = fTimestampBase + timestampIncrement;
#ifdef DEBUG_TIMESTAMPS
	fprintf(stderr, "fTimestampBase: 0x%08x, tv: %lu.%06ld\n\t=> RTP timestamp: 0x%08x\n",
		fTimestampBase, tv.tv_sec, tv.tv_usec, rtpTimestamp);
	fflush(stderr);
#endif

	return rtpTimestamp;
}

u_int32_t RTPSink::presetNextTimestamp() {
	struct timeval timeNow;
	gettimeofday(&timeNow, NULL);

	u_int32_t tsNow = convertToRTPTimestamp(timeNow);
	fTimestampBase = tsNow;
	fNextTimestampHasBeenPreset = True;

	return tsNow;
}

void RTPSink::getTotalBitrate(unsigned& outNumBytes, double& outElapsedTime) {
	struct timeval timeNow;
	gettimeofday(&timeNow, NULL);

	outNumBytes = fTotalOctetCount;
	outElapsedTime = (double)(timeNow.tv_sec-fTotalOctetCountStartTime.tv_sec)
		+ (timeNow.tv_usec-fTotalOctetCountStartTime.tv_usec)/1000000.0;

	fTotalOctetCount = 0;
	fTotalOctetCountStartTime = timeNow;
}

void RTPSink::resetPresentationTimes() {
	fInitialPresentationTime.tv_sec = fMostRecentPresentationTime.tv_sec = 0;
	fInitialPresentationTime.tv_usec = fMostRecentPresentationTime.tv_usec = 0;
}

char const* RTPSink::sdpMediaType() const {
	return "data";
	// default SDP media (m=) type, unless redefined by subclasses
}

char* RTPSink::rtpmapLine() const {
	if (rtpPayloadType() >= 96) { // the payload format type is dynamic
		char* encodingParamsPart;
		if (numChannels() != 1) {
			encodingParamsPart = new char[1 + 20 /* max int len */];
			sprintf(encodingParamsPart, "/%d", numChannels());
		} else {
			encodingParamsPart = strDup("");
		}
		char const* const rtpmapFmt = "a=rtpmap:%d %s/%d%s\r\n";
		unsigned rtpmapFmtSize = strlen(rtpmapFmt)
			+ 3 /* max char len */ + strlen(rtpPayloadFormatName())
			+ 20 /* max int len */ + strlen(encodingParamsPart);
		char* rtpmapLine = new char[rtpmapFmtSize];
		sprintf(rtpmapLine, rtpmapFmt,
			rtpPayloadType(), rtpPayloadFormatName(),
			rtpTimestampFrequency(), encodingParamsPart);
		delete[] encodingParamsPart;

		return rtpmapLine;
	} else {
		// The payload format is staic, so there's no "a=rtpmap:" line:
		return strDup("");
	}
}

char const* RTPSink::auxSDPLine() {
	return NULL; // by default
}


////////// RTPTransmissionStatsDB //////////

RTPTransmissionStatsDB::RTPTransmissionStatsDB(RTPSink& rtpSink)
	: fOurRTPSink(rtpSink),
	fTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
		fNumReceivers=0;
}

RTPTransmissionStatsDB::~RTPTransmissionStatsDB() {
	// First, remove and delete all stats records from the table:
	RTPTransmissionStats* stats;
	while ((stats = (RTPTransmissionStats*)fTable->RemoveNext()) != NULL) {
		delete stats;
	}

	// Then, delete the table itself:
	delete fTable;
}

void RTPTransmissionStatsDB
	::noteIncomingRR(u_int32_t SSRC, struct sockaddr_in const& lastFromAddress,
	unsigned lossStats, unsigned lastPacketNumReceived,
	unsigned jitter, unsigned lastSRTime, unsigned diffSR_RRTime) {
		RTPTransmissionStats* stats = lookup(SSRC);
		if (stats == NULL) {
			// This is the first time we've heard of this SSRC.
			// Create a new record for it:
			stats = new RTPTransmissionStats(fOurRTPSink, SSRC);
			if (stats == NULL) return;
			add(SSRC, stats);
#ifdef DEBUG_RR
			fprintf(stderr, "Adding new entry for SSRC %x in RTPTransmissionStatsDB\n", SSRC);
#endif
		}

		stats->noteIncomingRR(lastFromAddress,
			lossStats, lastPacketNumReceived, jitter,
			lastSRTime, diffSR_RRTime);
}

void RTPTransmissionStatsDB::removeRecord(u_int32_t SSRC) {
	RTPTransmissionStats* stats = lookup(SSRC);
	if (stats != NULL) {
		long SSRC_long = (long)SSRC;
		fTable->Remove((char const*)SSRC_long);
		--fNumReceivers;
		delete stats;
	}
}

RTPTransmissionStatsDB::Iterator
	::Iterator(RTPTransmissionStatsDB& receptionStatsDB)
	: fIter(HashTable::Iterator::create(*(receptionStatsDB.fTable))) {
}

RTPTransmissionStatsDB::Iterator::~Iterator() {
	delete fIter;
}

RTPTransmissionStats*
	RTPTransmissionStatsDB::Iterator::next() {
		char const* key; // dummy

		return (RTPTransmissionStats*)(fIter->next(key));
}

RTPTransmissionStats* RTPTransmissionStatsDB::lookup(u_int32_t SSRC) const {
	long SSRC_long = (long)SSRC;
	return (RTPTransmissionStats*)(fTable->Lookup((char const*)SSRC_long));
}

void RTPTransmissionStatsDB::add(u_int32_t SSRC, RTPTransmissionStats* stats) {
	long SSRC_long = (long)SSRC;
	fTable->Add((char const*)SSRC_long, stats);
	++fNumReceivers;
}


////////// RTPTransmissionStats //////////

RTPTransmissionStats::RTPTransmissionStats(RTPSink& rtpSink, u_int32_t SSRC)
	: fOurRTPSink(rtpSink), fSSRC(SSRC), fLastPacketNumReceived(0),
	fPacketLossRatio(0), fTotNumPacketsLost(0), fJitter(0),
	fLastSRTime(0), fDiffSR_RRTime(0), fAtLeastTwoRRsHaveBeenReceived(False), fFirstPacket(True),
	fTotalOctetCount_hi(0), fTotalOctetCount_lo(0),
	fTotalPacketCount_hi(0), fTotalPacketCount_lo(0) {
		gettimeofday(&fTimeCreated, NULL);

		fLastOctetCount = rtpSink.octetCount();
		fLastPacketCount = rtpSink.packetCount();
}

RTPTransmissionStats::~RTPTransmissionStats() {}

void RTPTransmissionStats
	::noteIncomingRR(struct sockaddr_in const& lastFromAddress,
	unsigned lossStats, unsigned lastPacketNumReceived,
	unsigned jitter, unsigned lastSRTime,
	unsigned diffSR_RRTime) {
		if (fFirstPacket) {
			fFirstPacket = False;
			fFirstPacketNumReported = lastPacketNumReceived;
		} else {
			fAtLeastTwoRRsHaveBeenReceived = True;
			fOldLastPacketNumReceived = fLastPacketNumReceived;
			fOldTotNumPacketsLost = fTotNumPacketsLost;
		}
		gettimeofday(&fTimeReceived, NULL);

		fLastFromAddress = lastFromAddress;
		fPacketLossRatio = lossStats>>24;
		fTotNumPacketsLost = lossStats&0xFFFFFF;
		fLastPacketNumReceived = lastPacketNumReceived;
		fJitter = jitter;
		fLastSRTime = lastSRTime;
		fDiffSR_RRTime = diffSR_RRTime;
#ifdef DEBUG_RR
		fprintf(stderr, "RTCP RR data (received at %lu.%06ld): lossStats 0x%08x, lastPacketNumReceived 0x%08x, jitter 0x%08x, lastSRTime 0x%08x, diffSR_RRTime 0x%08x\n",
			fTimeReceived.tv_sec, fTimeReceived.tv_usec, lossStats, lastPacketNumReceived, jitter, lastSRTime, diffSR_RRTime);
		unsigned rtd = roundTripDelay();
		fprintf(stderr, "=> round-trip delay: 0x%04x (== %f seconds)\n", rtd, rtd/65536.0);
#endif

		// Update our counts of the total number of octets and packets sent towards
		// this receiver:
		u_int32_t newOctetCount = fOurRTPSink.octetCount();
		u_int32_t octetCountDiff = newOctetCount - fLastOctetCount;
		fLastOctetCount = newOctetCount;
		u_int32_t prevTotalOctetCount_lo = fTotalOctetCount_lo;
		fTotalOctetCount_lo += octetCountDiff;
		if (fTotalOctetCount_lo < prevTotalOctetCount_lo) { // wrap around
			++fTotalOctetCount_hi;
		}

		u_int32_t newPacketCount = fOurRTPSink.packetCount();
		u_int32_t packetCountDiff = newPacketCount - fLastPacketCount;
		fLastPacketCount = newPacketCount;
		u_int32_t prevTotalPacketCount_lo = fTotalPacketCount_lo;
		fTotalPacketCount_lo += packetCountDiff;
		if (fTotalPacketCount_lo < prevTotalPacketCount_lo) { // wrap around
			++fTotalPacketCount_hi;
		}
}

unsigned RTPTransmissionStats::roundTripDelay() const {
	// Compute the round-trip delay that was indicated by the most recently-received
	// RTCP RR packet.  Use the method noted in the RTP/RTCP specification (RFC 3350).

	if (fLastSRTime == 0) {
		// Either no RTCP RR packet has been received yet, or else the
		// reporting receiver has not yet received any RTCP SR packets from us:
		return 0;
	}

	// First, convert the time that we received the last RTCP RR packet to NTP format,
	// in units of 1/65536 (2^-16) seconds:
	unsigned lastReceivedTimeNTP_high
		= fTimeReceived.tv_sec + 0x83AA7E80; // 1970 epoch -> 1900 epoch
	double fractionalPart = (fTimeReceived.tv_usec*0x0400)/15625.0; // 2^16/10^6
	unsigned lastReceivedTimeNTP
		= (unsigned)((lastReceivedTimeNTP_high<<16) + fractionalPart + 0.5);

	int rawResult = lastReceivedTimeNTP - fLastSRTime - fDiffSR_RRTime;
	if (rawResult < 0) {
		// This can happen if there's clock drift between the sender and receiver,
		// and if the round-trip time was very small.
		rawResult = 0;
	}
	return (unsigned)rawResult;
}

void RTPTransmissionStats::getTotalOctetCount(u_int32_t& hi, u_int32_t& lo) {
	hi = fTotalOctetCount_hi;
	lo = fTotalOctetCount_lo;
}

void RTPTransmissionStats::getTotalPacketCount(u_int32_t& hi, u_int32_t& lo) {
	hi = fTotalPacketCount_hi;
	lo = fTotalPacketCount_lo;
}

unsigned RTPTransmissionStats::packetsReceivedSinceLastRR() const {
	if (!fAtLeastTwoRRsHaveBeenReceived) return 0;

	return fLastPacketNumReceived-fOldLastPacketNumReceived;
}

int RTPTransmissionStats::packetsLostBetweenRR() const {
	if (!fAtLeastTwoRRsHaveBeenReceived) return 0;

	return fTotNumPacketsLost - fOldTotNumPacketsLost;
}
